package com.xuhj.readexcel.factory;

import com.xuhj.common.CommonUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class ReadExcelFactory {
    /**
     * 读取 Excel 数据并处理
     *
     * @param workbook 完整的 Workbook 对象
     * @param clazz    Excel 中存储的数据的类的 Class 对象
     * @param <T>      泛型
     * @return 解析之后的对象列表，与泛型一致
     * @throws Exception
     */
    public static <T> List<T> readExcel(Workbook workbook, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<>();
        Sheet sheet = workbook.getSheetAt(0);
        //获取总行数
        int rows = sheet.getPhysicalNumberOfRows();
        //获取所有字段名
        String[] fieldsName = CommonUtils.getFieldsName(clazz);
        Method[] methods = new Method[fieldsName.length];
        //去除表头，从第 1 行开始打印
        for (int i = 1; i < rows; i++) {
            T obj = clazz.newInstance();
            Row row = sheet.getRow(i);
            //获取总列数
            int cols = row.getPhysicalNumberOfCells();
            //获取所有属性
            Field[] fields = clazz.getDeclaredFields();
            //处理对象的每一个属性
            for (int j = 0; j < cols; j++) {
                //第一次循环时初始化所有 setter 方法名
                if (i == 1) {
                    String fieldName = fieldsName[j];
                    //处理布尔值命名 "isXxx" -> "setXxx"
                    if (fieldName.contains("is")) {
                        fieldName = fieldName.split("is")[1];
                    }
                    methods[j] = obj.getClass().getMethod("set" +
                            fieldName.substring(0, 1).toUpperCase() +
                            fieldName.substring(1), fields[j].getType());
                }
                //先将单元格中的值按 String 保存
                String param = row.getCell(j).getStringCellValue();
                //属性的类型
                String typeName = fields[j].getType().getSimpleName();
                //set 方法
                Method method = methods[j];
                //排除空值
                if (param == null || "".equals(param)) {
                    continue;
                }
                //根据对象的不同属性字段转换单元格中的数据类型并调用 set 方法赋值
                if ("Integer".equals(typeName) || "int".equals(typeName)) {
                    method.invoke(obj, Integer.parseInt(param));
                } else if ("Date".equals(typeName)) {
                    String pattern;
                    if (param.contains("CST")) {
                        //java.util.Date 的默认格式
                        pattern = "EEE MMM dd HH:mm:ss zzz yyyy";
                    } else if (param.contains(":")) {
                        //带有时分秒的格式
                        pattern = "yyyy-MM-dd HH:mm:ss";
                    } else {
                        //简单格式
                        pattern = "yyyy-MM-dd";
                    }
                    method.invoke(obj, new SimpleDateFormat(pattern, Locale.UK).parse(param));
                } else if ("Long".equalsIgnoreCase(typeName)) {
                    method.invoke(obj, Long.parseLong(param));
                } else if ("Double".equalsIgnoreCase(typeName)) {
                    method.invoke(obj, Double.parseDouble(param));
                } else if ("Boolean".equalsIgnoreCase(typeName)) {
                    method.invoke(obj, Boolean.parseBoolean(param));
                } else if ("Short".equalsIgnoreCase(typeName)) {
                    method.invoke(obj, Short.parseShort(param));
                } else if ("Character".equals(typeName) || "char".equals(typeName)) {
                    method.invoke(obj, param.toCharArray()[0]);
                } else {
                    //若数据格式为 String 则不必转换
                    method.invoke(obj, param);
                }
            }
            //不要忘记这句
            list.add(obj);
        }
        return list;
    }

}
